Skip to content

The interview now says what the two answers changed - #61

Merged
TruongGiang2000 merged 2 commits into
mainfrom
fix/interview-close-says-what-it-changed
Aug 4, 2026
Merged

The interview now says what the two answers changed#61
TruongGiang2000 merged 2 commits into
mainfrom
fix/interview-close-says-what-it-changed

Conversation

@TruongGiang2000

Copy link
Copy Markdown
Collaborator

Found by running the app — which nothing else on the feature branch could do.

The founder answered "how long does your money last?" and "any constraint the room must not propose?", and then the conversation just stopped. No acknowledgement, no visible change. Their words: it feels like it did nothing.

That reading was fair.

Why it was silent

The first-run interview ends by seeding byte's greeting. This one deliberately suppresses that — welcoming a founder right after a mid-session pricing question would read as amnesia — and nothing was put in its place:

} else {
    interviewState = nil
    if st.seedGreetingWhenDone { seedFirstRunGreeting(language: language) }
}

The chat was not blocked, either: isChatBusy is isCompanionTyping || isStreaming || isFanningOut, and the interview sets none of them. It was silent, not stuck — which is harder to diagnose as a user, because nothing looks broken.

What the close does

It quotes both answers back rather than thanking the founder abstractly, so the specifics are visibly heard, and it names what they change: the room drops proposals that eat too much of the remaining runway, and stops suggesting what has already been ruled out. A founder has no way to infer that runway is the thing that makes a three-week proposal unacceptable.

Skipping both is handled separately and honestly — it says recommendations stay more general, rather than claiming an effect it did not get.

A test that asserted more than it meant

One existing test proved "no greeting" by checking the last message's role was .me, conflating no welcome with no companion message at all. Narrowed to assert what it means: that this particular greeting text is absent. Its own comment already recorded being rewritten once before, for the same reason.

Verified

VirtualCompanyInterviewTests 19/19 (2 new) · CompanyStoreChatTests 24/24 · CompanyStoreOnboardingTests 11/11 · EnrichInterviewTests 8/8 · CompanyStoreVirtualCompanyTests 15/15 · build green.

The larger finding this exposed, not fixed here

runway and constraints are invisible after they are answered — but so are goal, traction and problem, the three fields the existing onboarding interview collects. None of the five appear anywhere in the UI; they only feed prompt context. The brief editor in Settings exposes founderName, role, tech, stage, oneLiner, audience and nothing the interviews gather.

So the pattern is already ask → the answer dissolves into context → the founder never sees it again. This feature only made it visible, by asking mid-session where the silence is noticeable rather than during onboarding where it is not.

A small "what the room knows about you" surface would fix all five, and is a product decision worth making deliberately rather than as a side effect of this PR.

🤖 Generated with Claude Code

TruongGiang2000 and others added 2 commits August 4, 2026 15:25
Found by running the app, which nothing else on this branch could do. The
founder answered "how long does your money last" and "any constraint the room
must not propose", and then the conversation simply stopped. No acknowledgement,
no visible change. Their words: it feels like it did nothing.

That reading was fair. The first-run interview ends by seeding byte's greeting;
this one deliberately suppresses that — and nothing was put in its place. Neither
answer is displayed anywhere in the app, so a closing line is the only evidence
they landed at all.

The close quotes both answers back rather than thanking the founder abstractly,
because the point is to show the specifics were heard, and it names what they
change: the room drops proposals that eat too much of the runway, and stops
suggesting what has already been ruled out. A founder has no way to infer that
runway is what makes a three-week proposal unacceptable.

Skipping both is handled separately and honestly — it says recommendations stay
more general, rather than claiming an effect it did not get.

One existing test asserted "no greeting" by checking the last message's role was
.me, which conflated "no welcome" with "no companion message at all". Narrowed to
assert what it means: that this particular greeting text is absent. Its comment
already recorded being rewritten once before for the same reason.

VirtualCompanyInterviewTests 19/19 (2 new), CompanyStoreChatTests 24/24,
CompanyStoreOnboardingTests 11/11, EnrichInterviewTests 8/8,
CompanyStoreVirtualCompanyTests 15/15, build green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Found in the app. byte replied with a run_task_id and no text, so the store wrote
its lead-in — "on it, putting that together now" — and then nothing ever arrived.
No draft, no error, no explanation. Forever.

`handleRunTaskId` had three silent early returns, and the lead-in is written
BEFORE any of them run:

    guard let runId,
          let task = company.tasks.first(where: { $0.id == runId }),
          RoadmapEngine.status(for: task, in: company.tasks) == .codepetCanDo
    else { return }

So byte promised and a guard quietly withdrew it. Worth noting `produceDraftInline`
already had an honest failure path — its own comment promises "an honest couldn't
generate bubble" — but this returned before reaching it.

Every refusal knows exactly why, and `TaskStatus` distinguishes five cases, three
of which are the founder's own move rather than a failure. So each one says so, and
names the task: the founder did not choose it, byte did, so "that one" is not
enough to act on.

- needsApproval → a draft is already waiting; redoing it would overwrite that
- done → already finished; open it if you want it redone
- blocked → waiting on another task, or its phase hasn't opened
- needsYou → yours to do, and byte offers to walk you through it
- task missing → the id no longer matches the roadmap; ask which one

A nil run_task_id — the overwhelmingly common case — still says nothing.

Two existing tests asserted `chatMessages.count == 2` with the comment "me +
lead-in only": they encoded the silence itself. Updated to assert the reason is
given, plus a new test covering all four refusal reasons and that each names its
task.

CompanyStoreChatRunTests 15/15, CompanyStoreChatTests 24/24,
CompanyStoreRunTaskTests 17/17, CompanyStoreVirtualCompanyTests 15/15,
VirtualCompanyInterviewTests 19/19, ChatTailActionTests 5/5, build green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TruongGiang2000
TruongGiang2000 merged commit 7ac8f14 into main Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant